package com.wxy.service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wxy.config.redisConfig;
import com.wxy.dao.mybatisPlusDemo;
import com.wxy.dao.mybatisPlusDemo2;
import com.wxy.dao.userMapper;
import com.wxy.domain.MyExceptionHandle;
import com.wxy.domain.bank;
import com.wxy.domain.redisUser;
import com.wxy.domain.user;
import com.wxy.domain.user2;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;

@Component("service")
public class serviceImpl implements service {
	@Autowired
	private StringRedisTemplate stringRedisTemplate;
	@Autowired
	private userMapper userMapper;
	@Autowired
	private mybatisPlusDemo mybatisPlusDemo;
	@Autowired
	private mybatisPlusDemo2 mybatisPlusDemo2;
	public List<user2> findAll(){
		return null;
	}
	public List<user> findUsers(){
		List<user> users=this.mybatisPlusDemo.selectList(null);
		return users;
	}
	public List<bank> findBanks(){
		QueryWrapper<bank> queryWrapper=new QueryWrapper<bank>();
		queryWrapper.ge("id",2);
		return this.mybatisPlusDemo2.selectList(queryWrapper);
	}
	@Override
	public user2 getUserById(int id){		//添加缓存策略以及解决了缓存穿透问题
		// TODO 自动生成的方法存根
		String userKeyString="user:"+id;
		//从redis查询出json字符串
		String jsonString=stringRedisTemplate.opsForValue().get(userKeyString);
		//判断字符串中是否不为空且是否有有效值
		if(StrUtil.isNotBlank(jsonString))
			return JSONUtil.toBean(jsonString,user2.class);
		//字符串的值为""
		if(jsonString!=null)
			throw new MyExceptionHandle("该用户不存在...");
		//为空，查询数据库
		user2 user2=userMapper.findById(id);
		System.out.println("数据库查询了...");
		//数据库查询为空，发生缓存穿透问题，将空字符串返回给redis
		if(user2==null) {
			stringRedisTemplate.opsForValue().set(userKeyString, "", 3, TimeUnit.MINUTES);
			throw new MyExceptionHandle("该用户不存在");}
		//不为空，返回user实例给redis
		stringRedisTemplate.opsForValue().set(userKeyString,JSONUtil.toJsonStr(user2) , 30, TimeUnit.MINUTES);
		return user2;
	}
	public user2 getUserById2(int id){		//添加缓存策略以及解决了缓存穿透问题
		// TODO 自动生成的方法存根
		String userKeyString="user:"+id;
		//从redis查询出json字符串
		String jsonString=stringRedisTemplate.opsForValue().get(userKeyString);
		//判断字符串中是否不为空且是否有有效值
		if(jsonString!=null&&jsonString.length()!=0)
			return JSONUtil.toBean(jsonString,user2.class);
		//字符串的值为""
		if(jsonString!=null)
			throw new MyExceptionHandle("该用户不存在...");
		//为空，查询数据库
		user2 user2=userMapper.findById(id);
		System.out.println("数据库查询了...");
		//数据库查询为空，发生缓存穿透问题，将空字符串返回给redis
		if(user2==null) {
			stringRedisTemplate.opsForValue().set(userKeyString, "", 3, TimeUnit.MINUTES);
			throw new MyExceptionHandle("该用户不存在");}
		//不为空，返回user实例给redis
		stringRedisTemplate.opsForValue().set(userKeyString,JSONUtil.toJsonStr(user2) , 30, TimeUnit.MINUTES);
		return user2;
	}
	@Transactional
	@Override							//缓存更新策略		删除缓存，先更新数据库，后删除redis
	public boolean updateById(int id, String username) {
		// TODO 自动生成的方法存根
		//1.先进行数据库的数据更新，后删除缓存
		String userKeyString="user:"+id;
		int n= userMapper.updateById(id, username);
		if(n!=0) {
			stringRedisTemplate.delete(userKeyString);
			return true;
		}
		else {
			return false;
		}
	}
	@Override
	public user2 findById(int id) {						//缓存击穿问题，使用锁和线程休眠
		// TODO 自动生成的方法存根
		String userKeyString="user:"+id;
		//从redis查询出json字符串
		String jsonString=stringRedisTemplate.opsForValue().get(userKeyString);
		//判断字符串中是否不为空且是否有有效值
		if(StrUtil.isNotBlank(jsonString))
			return JSONUtil.toBean(jsonString,user2.class);
		//字符串的值为""
		if(jsonString!=null)
			throw new MyExceptionHandle("该用户不存在...");
		user2 user=new user2();
		//3.redis中不存在数据，判断锁是否存在
		try {
			boolean b=addScore(id);
			//4.不存在，休眠，函数递归
			if(!b) {
				Thread.sleep(100);
				return findById(id);
			}
			//5.锁存在，加锁，查询数据库，返回redis数据
			user=userMapper.findById(id);
			System.out.println("数据库查询了...");
			//数据库查询为空，发生缓存穿透问题，将空字符串返回给redis
			if(user==null) {
				stringRedisTemplate.opsForValue().set(userKeyString, "", 3, TimeUnit.MINUTES);
				throw new MyExceptionHandle("该用户不存在");
				}
			//不为空，返回user实例给redis
			stringRedisTemplate.opsForValue().set(userKeyString,JSONUtil.toJsonStr(user) , 30, TimeUnit.MINUTES);}
		catch (InterruptedException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
		finally {
			delScore(id);
		}
		return user;
	}
	private boolean addScore(int id) {
		boolean b= stringRedisTemplate.opsForValue().setIfAbsent("user:stop"+id,"1",2,TimeUnit.MINUTES);
		return b;
	}
	private void delScore(int id) {
		stringRedisTemplate.delete("user:stop"+id);
	}
	public user2 findById2(int id) {		//使用逻辑过期解决缓存击穿问题
		//1.判断是否存在redis
		String string="user:"+id;
		String jsonString=stringRedisTemplate.opsForValue().get(string);
		if(StrUtil.isBlank(jsonString))
		//2.不存在，返回
			throw new MyExceptionHandle("该用户不存在");
		//3.存在，反序列化为java对象
		redisUser redisUser=JSONUtil.toBean(jsonString, redisUser.class);
		JSONObject jsonObject=(JSONObject) redisUser.getUser();
		user2 user=new user2();
		user=JSONUtil.toBean(jsonObject,user2.class);
		LocalDateTime localDateTime=redisUser.getLocalDateTime();
		//4.判断是否过期，未过期，返回数据
		if(localDateTime.isAfter(localDateTime.now())) {
			return user;
		}
		//5.过期，缓存重建
		//5.1.获取互斥锁
		boolean b=addScore(id);
		//5.2.判断是否获取成功
		if(b==true) {
			//5.3.成功，开启独立线程，实现缓存重建
			Thread thread=new Thread();
			thread.start();
			System.out.println("开启新线程...");
			user=userMapper.findById(id);
			System.out.println("数据库查询了...");
			redisUser redisUser2=new redisUser(user, localDateTime.now().plusMinutes(2));
			stringRedisTemplate.opsForValue().set(string,JSONUtil.toJsonStr(redisUser2));
			delScore(id);
			return user;
		}
		//5.4.获取锁失败，返回旧数据
		return user;
	}
}
